home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / xulrunner / python / rdfa.py < prev    next >
Encoding:
Python Source  |  2008-01-10  |  7.5 KB  |  246 lines

  1. #!/usr/bin/python2.4
  2. """
  3. RDFa parser. 
  4.  
  5. RDFa is a set of attributes used to embed RDF in XHTML. An important goal of
  6. RDFa is to achieve this RDF embedding without repeating existing XHTML content
  7. when that content is the metadata.  
  8.  
  9. REFERENCES:
  10.  
  11.     http://www.w3.org/2001/sw/BestPractices/HTML/2005-rdfa-syntax
  12.  
  13. Copyright (c) 2006, Elias Torres <elias@torrez.us>
  14. Licensed to the public under the GNU GPL v2.
  15.  
  16. """
  17.  
  18. import sys, re, urllib, urlparse, cStringIO
  19. from xml.dom import pulldom
  20.  
  21. __version__ = "$Id: rdfa.py 118 2006-06-03 18:35:18Z eliast $"
  22.  
  23. rdfa_attribs = ["about","property","rel","rev","href","content"]
  24.  
  25. class NS(unicode): 
  26.   def __getattr__(self, name): return self + name
  27.  
  28. xhtml = NS("http://www.w3.org/1999/xhtml")
  29. xml = NS("http://www.w3.org/XML/1998/namespace")
  30. rdf = NS("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
  31.  
  32. class Node(unicode): pass
  33. class URI(Node): pass
  34. class bNode(Node): pass
  35. class Literal(Node):
  36.   def __new__(cls, lit, lang=None, dtype=None):
  37.     n = "\"" + lit + "\""
  38.     if not lang is None:
  39.       n += "@" + str(lang)
  40.     elif not dtype is None:
  41.       n += "^^<" + str(dtype) + ">"
  42.     return unicode.__new__(cls, n)
  43.  
  44. _urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
  45. def _urljoin(base, uri):
  46.   uri = _urifixer.sub(r'\1\3', uri)
  47.   return urlparse.urljoin(base, uri)
  48.  
  49. class RDFaParser:
  50.   def __init__(self, sink, base=None, lang=None): 
  51.     self.triple = sink.triple
  52.     self.baseuri = base or ''
  53.     self.lang = lang or None
  54.     self.abouts = []
  55.     self.xmlbases = []
  56.     self.langs = []
  57.     self.elementStack = [None]
  58.     self.bcounter = {}
  59.     self.bnodes = {}
  60.  
  61.   def generateBlankNode(self, parentNode):
  62.     name = parentNode.tagName
  63.  
  64.     if self.bnodes.has_key(parentNode):
  65.       return self.bnodes[parentNode]
  66.     
  67.     if self.bcounter.has_key(name):
  68.       self.bcounter[name] = self.bcounter[name] + 1
  69.     else:
  70.       self.bcounter[name] = 0
  71.  
  72.     self.bnodes[parentNode] = bNode("_:%s%d" % (name, self.bcounter[name]))
  73.  
  74.     return self.bnodes[parentNode]
  75.  
  76.   def extractCURIEorURI(self, resource):
  77.     if(len(resource) > 0 and resource[0] == "[" and resource[-1] == "]"):
  78.       resource = resource[1:-1]
  79.  
  80.     # resolve prefixes
  81.     # TODO: check whether I need to reverse the ns_contexts
  82.     if(resource.find(":") > -1):
  83.       rpre,rsuf = resource.split(":", 1)
  84.       for nsc in self.handler._ns_contexts:
  85.         for ns, prefix in nsc.items():
  86.           if prefix == rpre:
  87.             resource = ns + rsuf
  88.  
  89.     # is this enough to check for bnodes?
  90.     if(len(resource) > 0 and resource[0:2] == "_:"):
  91.       return bNode(resource)
  92.  
  93.     return URI(self.resolveURI(resource))
  94.  
  95.   def resolveURI(self, uri):
  96.     return _urljoin(self.baseuri or '', uri)
  97.  
  98.   def _popStacks(self, event, node):
  99.     # check abouts
  100.     if len(self.abouts) <> 0:
  101.       about, aboutnode = self.abouts[-1]
  102.       if aboutnode == node:
  103.         self.abouts.pop()
  104.  
  105.     # keep track of nodes going out of scope
  106.     self.elementStack.pop()
  107.  
  108.     # track xml:base and xml:lang going out of scope
  109.     if self.xmlbases:
  110.       self.xmlbases.pop()
  111.       if self.xmlbases and self.xmlbases[-1]:
  112.         self.baseuri = self.xmlbases[-1]
  113.  
  114.     if self.langs:
  115.       self.langs.pop()
  116.       if self.langs and self.langs[-1]:
  117.         self.lang = self.langs[-1]
  118.  
  119.   def parse(self, stream):
  120.     events = pulldom.parse(stream)
  121.     self.handler = events.pulldom
  122.     for (event, node) in events:
  123.  
  124.       if event == pulldom.START_DOCUMENT:
  125.         self.abouts += [(URI(""), node)]
  126.  
  127.       if event == pulldom.END_DOCUMENT:
  128.         assert len(self.elementStack) == 0
  129.       
  130.       if event == pulldom.START_ELEMENT:
  131.  
  132.         # keep track of parent node
  133.         self.elementStack += [node]
  134.  
  135.         #if __debug__: print [e.tagName for e in self.elementStack if e]
  136.  
  137.         found = filter(lambda x:x in node.attributes.keys(),rdfa_attribs)
  138.  
  139.         # keep track of xml:lang xml:base
  140.         baseuri = node.getAttributeNS(xml,"base") or node.getAttribute("base") or self.baseuri
  141.         self.baseuri = _urljoin(self.baseuri, baseuri)
  142.         self.xmlbases.append(self.baseuri)
  143.  
  144.         if node.hasAttributeNS(xml,"lang") or node.hasAttribute("lang"):
  145.           lang = node.getAttributeNS(xml, 'lang') or node.getAttribute('lang')
  146.           if lang == '':
  147.             # xml:lang could be explicitly set to '', we need to capture that
  148.             lang = None
  149.         else:
  150.           # if no xml:lang is specified, use parent lang
  151.           lang = self.lang
  152.  
  153.         self.lang = lang
  154.         self.langs.append(lang)
  155.  
  156.         # node is not an RDFa element.
  157.         if len(found) == 0: continue
  158.  
  159.         parentNode = self.elementStack[-2]
  160.  
  161.         if "about" in found:
  162.           self.abouts += [(self.extractCURIEorURI(node.getAttribute("about")),node)]
  163.  
  164.         subject = self.abouts[-1][0]
  165.  
  166.         # meta/link subject processing
  167.         if(node.tagName == "meta" or node.tagName == "link"):
  168.           if not("about" in found) and parentNode:
  169.             if(parentNode.hasAttribute("about")):
  170.               subject = self.extractCURIEorURI(parentNode.getAttribute("about"))
  171.             elif parentNode.hasAttributeNS(xml,"id") or parentNode.hasAttribute("id"):
  172.               # TODO: is this the right way to process xml:id by adding a '#'
  173.               id = parentNode.getAttributeNS(xml,"id") or parentNode.getAttribute("id")
  174.               subject = self.extractCURIEorURI("#" + id)
  175.             else:
  176.               subject = self.generateBlankNode(parentNode)
  177.  
  178.         if 'property' in found:
  179.           predicate = self.extractCURIEorURI(node.getAttribute('property'))
  180.           literal = None
  181.           datatype = None
  182.  
  183.           if node.hasAttribute('datatype'):
  184.             datatype = self.extractCURIEorURI(node.getAttribute('datatype'))
  185.             if datatype == 'plaintext':
  186.               datatype = None
  187.  
  188.           if node.hasAttribute("content"):
  189.             literal = Literal(node.getAttribute("content"), lang=lang, dtype=datatype)
  190.           else:
  191.             events.expandNode(node)
  192.  
  193.             # because I expanded, I won't get an END_ELEMENT
  194.             self._popStacks(event, node)
  195.  
  196.             content = ""
  197.             for child in node.childNodes:
  198.               content += child.toxml()
  199.             content = content.strip()
  200.             literal = Literal(content,dtype=rdf.XMLLiteral) 
  201.           
  202.           if literal:
  203.             self.triple(subject, predicate, literal)
  204.  
  205.         if "rel" in found:
  206.           predicate = self.extractCURIEorURI(node.getAttribute("rel"))
  207.           if node.hasAttribute("href"):
  208.             object = self.extractCURIEorURI(node.getAttribute("href"))
  209.             self.triple(subject, predicate, object)
  210.  
  211.         if "rev" in found:
  212.           predicate = self.extractCURIEorURI(node.getAttribute("rev"))
  213.           if node.hasAttribute("href"):
  214.             object = self.extractCURIEorURI(node.getAttribute("href"))
  215.             self.triple(object, predicate, subject)
  216.  
  217.       if event == pulldom.END_ELEMENT:
  218.         self._popStacks(event, node)
  219.  
  220. class Sink(object): 
  221.   def __init__(self): 
  222.     self.result = ""
  223.   def __str__(self): 
  224.     return self.result
  225.   def triple(self, s, p, o): 
  226.     if o.__class__ is URI:
  227.       o = "<" + o + ">"
  228.     if s.__class__ is URI:
  229.       s = "<" + s + ">"
  230.     self.result += "%s <%s> %s .\n" % (s, p, o)
  231.  
  232. def parseRDFa(s, base=None, sink=None): 
  233.   print 'sink is ', sink
  234.  
  235.   sink = sink or Sink()
  236.   parser = RDFaParser(sink, base)
  237.   parser.parse(cStringIO.StringIO(s))
  238.   return sink
  239.  
  240. def parseURI(uri, sink=None): 
  241.   return parseRDFa(urllib.urlopen(uri).read(), base=uri, sink=sink)
  242.  
  243. if __name__=="__main__": 
  244.   if len(sys.argv) != 2: print __doc__
  245.   else: print parseURI(sys.argv[1])
  246.